package com.netty.exmple.filecopy;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

public class TestCopy {
    public static String fileName = "D:\\softPackage\\ideaIU-2019.1.3.exe";
    public static String copyToFileName = "E:\\test\\ideaIU-2019.1.3.exe";
    public static Path copyFrom = Paths.get(fileName);
    public static Path copyTo = Paths.get(copyToFileName);
    public static long startTime, elapsedTime;
    public static int bufferSize;
    public static FileChannel fileChannelFrom;
    public static FileChannel fileChannelTo;
    public static BufferedInputStream bis;
    public static BufferedOutputStream bos;

    public static void main(String[] args) throws Exception {
        int bufferSizeKB = 64;
        bufferSize = bufferSizeKB * 1024;
        deleteCopied(copyTo);
        fileChannelFrom = FileChannel.open(copyFrom, EnumSet.of(StandardOpenOption.READ));
        fileChannelTo = FileChannel.open(copyTo, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE));

        System.out.println("Using FileChannel and non-direct buffer ");
        startTime = System.nanoTime();
        fileChannelNonDirectBuffer();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using FileChannel and direct buffer");
        fileChannelTo = FileChannel.open(copyTo, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE));
        startTime = System.nanoTime();
        fileChannelDirectBuffer();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using FileChannel transferTo method");
        startTime = System.nanoTime();
        fileChannelTransferTo();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using FileChannel transferFrom method");
        startTime = System.nanoTime();
        fileChannelTransferFrom();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using FileChannel map method");
        startTime = System.nanoTime();
        fileChannelMap();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using buffer Stream IO");
        startTime = System.nanoTime();
        bufferStreamIO();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using non buffer Stream IO");
        startTime = System.nanoTime();
        unbufferStreamIO();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using Files.copy (Path to Path) method");
        startTime = System.nanoTime();
        fileCopyPathToPath();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using Files.copy (InputStream to Path)");
        startTime = System.nanoTime();
        inputStreamToPath();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

        deleteCopied(copyTo);

        System.out.println("Using Files.copy (Path to OutputStream)");
        startTime = System.nanoTime();
        pathToOutputStream();
        elapsedTime = System.nanoTime() - startTime;
        System.out.println("Elapsed time：" + elapsedTime);

    }

    /**
     * FileChannel and non-direct buffer
     */
    public static void fileChannelNonDirectBuffer() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(bufferSize);
        while (fileChannelFrom.read(byteBuffer) > 0) {
            byteBuffer.flip();
            fileChannelTo.write(byteBuffer);
            byteBuffer.clear();
        }
    }

    /**
     * FileChannel and direct buffer
     */
    public static void fileChannelDirectBuffer() throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize);
        while (fileChannelFrom.read(byteBuffer) > 0) {
            byteBuffer.flip();
            fileChannelTo.write(byteBuffer);
            byteBuffer.clear();
        }
    }

    /**
     * FileChannel.transferTo()
     */
    public static void fileChannelTransferTo() throws IOException {
        fileChannelFrom.transferTo(0L, fileChannelFrom.size(), fileChannelTo);
    }

    /**
     * FileChannel.transferFrom()
     */
    public static void fileChannelTransferFrom() throws IOException {
        fileChannelTo.transferFrom(fileChannelFrom, 0L, fileChannelFrom.size());
    }

    /**
     * FileChannel.map
     */
    public static void fileChannelMap() throws IOException {
        fileChannelFrom.map(FileChannel.MapMode.READ_ONLY, 0, fileChannelFrom.size());
    }

    /**
     * buffered streams and byte array
     */
    public static void bufferStreamIO() throws FileNotFoundException, IOException {
        bis = new BufferedInputStream(new FileInputStream(copyFrom.toFile()));
        bos = new BufferedOutputStream(new FileOutputStream(copyTo.toFile()));
        byte[] byteArray = new byte[bufferSize];
        int bytesCount;
        while ((bytesCount = bis.read(byteArray)) != -1) {
            bos.write(byteArray, 0, bytesCount);
        }
        bos.close();
        bis.close();
    }

    /**
     * un-buffered streams and byte array
     */
    public static void unbufferStreamIO() throws IOException {
        FileInputStream fileInputStream = new FileInputStream(copyFrom.toFile());
        FileOutputStream fileOutputStream = new FileOutputStream(copyTo.toFile());
        byte[] byteArray = new byte[bufferSize];
        int bytesCount;
        while ((bytesCount = fileInputStream.read(byteArray)) != -1) {
            fileOutputStream.write(byteArray, 0, bytesCount);
        }
        fileOutputStream.close();
        fileInputStream.close();
    }

    /**
     * Files.copy (Path to Path) method
     */
    public static void fileCopyPathToPath() throws IOException{
        Files.copy(copyFrom, copyTo, NOFOLLOW_LINKS);
    }

    /**
     * Files.copy (InputStream to Path)
     */
    public static void inputStreamToPath() throws IOException{
        InputStream is = new FileInputStream(copyFrom.toFile());
        Files.copy(is, copyTo);
        is.close();
    }

    /**
     * Files.copy (Path to OutputStream)
     */
    public static void pathToOutputStream() throws IOException{
        OutputStream os = new FileOutputStream(copyTo.toFile());
        Files.copy(copyFrom, os);
        os.close();
    }

    public static void deleteCopied(Path path) {
        try {
            Files.deleteIfExists(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
